package org.lex.collection.tree.diff;

import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;

import org.lex.collection.tree.ITreeNode;
import org.lex.collection.tree.TreeNode;

public class DiffSetUtils {
	public ITreeNode buildDiffTree(DiffSet results, INodeComparator comparator) {
		ITreeNode root = new TreeNode();
		// added
		this.buildSubTree("Add", root, results.added);
		// deleted
		this.buildSubTree("Delete", root, results.deleted);
		// updated
		List<ITreeNode> updated = new ArrayList<ITreeNode>();
		for (Diff<ITreeNode> diff : results.sameOrUpdate) {
			if (comparator.isUpdate(diff)) {
				updated.add(diff.newObject);
			}
		}

		root.setData("[" + results.added.size() + "+, " + results.deleted.size() + "-, " + updated.size() + "~]");
		this.buildSubTree("Update", root, updated);
		return root;
	}

	private void buildSubTree(Object name, ITreeNode root, Collection<ITreeNode> nodes) {
		ITreeNode subRoot = null;
		if (false == nodes.isEmpty()) {
			subRoot = new TreeNode(name);
			root.addChild(subRoot);
		}
		Map<ITreeNode, ITreeNode> old_new = new IdentityHashMap<ITreeNode, ITreeNode>();
		for (ITreeNode oldNode : nodes) {
			linkedToRoot(subRoot, oldNode, old_new);
		}
	}

	private void linkedToRoot(ITreeNode newRoot, ITreeNode oldNode, Map<ITreeNode, ITreeNode> map) {
		ITreeNode newNode = map.get(oldNode);
		if (null == newNode) {
			newNode = clone(oldNode);
			map.put(oldNode, newNode);
		}
		ITreeNode oldCurrentNode = oldNode;
		ITreeNode newCurrentNode = newNode;
		while (true) {
			ITreeNode oldParent = oldCurrentNode.getParent();
			if (null == oldParent) {
				newRoot.addChild(newCurrentNode);
				break;
			} else {
				ITreeNode newParent = map.get(oldParent);
				if (null == newParent) {
					newParent = clone(oldParent);
					map.put(oldParent, newParent);
				}
				newParent.addChild(newCurrentNode);
				newCurrentNode = newParent;
				oldCurrentNode = oldParent;
			}
		}
	}

	private ITreeNode clone(ITreeNode src) {
		ITreeNode copy = new TreeNode(src.getData());
		return copy;
	}
}
